#include "share.h"
#include "zqm_global.h"
#include "zqm_module.h"

// m0 spec parameters table(default). describe the parameters name, value(default), min, max, unit, map, strictly folllow the spec
// 1001 835 870 915 1198 1000 perm 设置为本地可以修改所有参数, 其他r1,r2, f5, f6本地不可修改
ZqmPmSpec pms_spec[] = {
//  {   name,   val,    min,    max,    unit,   perm	map(->pm_idx)	},
	{   NULL,   0x7fff,	0,		0,		0,		0,		0,				}, // this line preserved for catching invalid index, don't modified
	{   "Fr",   0,		0,		0,		PMU_NV, 0,		ZQM_PM_MAP(d8), },
	{   "r1",   -60,    -80,    -20,    PMU_C,  1,		ZQM_PM_MAP(r1), },
	{   "r2",   -20,    -20,    100,    PMU_C,  1,		ZQM_PM_MAP(r2), },
	{   "C0",   -20,    -60,    -20,    PMU_C,  1,		ZQM_PM_MAP(ts), },
	{   "Ad",   1,      0,      99,     PMU_M,  1,		ZQM_PM_MAP(ad), },
	{   "C1",   15,     0,      99,     PMU_N,  1,		ZQM_PM_MAP(c1), },
	{   "C2",   15,     0,      99,     PMU_N,  1,		ZQM_PM_MAP(c2), },
	{   "C3",   3,		0,      99,     PMU_M,  1,		ZQM_PM_MAP(c7), },
	{   "C4",   0,      0,      99,     PMU_S1, 1,		ZQM_PM_MAP(cd), },
	{   "C5",   0,      -99,    99,     PMU_N,  1,		ZQM_PM_MAP(mtc),},
	{   "C6",   10,     0,     	99,     PMU_M,  1,		ZQM_PM_MAP(c3), },
	{   "C7",   10,     0,      99,     PMU_M,  1,		ZQM_PM_MAP(c4), },
	{   "C8",   -20,    -99,    99,     PMU_N,  1,		ZQM_PM_MAP(c5), },
	{   "d0",   6,      0,      99,     PMU_H,  1,		ZQM_PM_MAP(d1), },
	{   "d1",   6,      0,      99,     PMU_H,  1,		ZQM_PM_MAP(d1), },
	{   "d2",   25,     1,      99,     PMU_M,  1,		ZQM_PM_MAP(d2), },
	{   "F1",   0,      0,      2,      PMU_F1, 0,		ZQM_PM_MAP(f1), },
	{   "F2",   0,      0,      2,      PMU_R, 	0,		ZQM_PM_MAP(f2), },
	{   "F5",   150,    -990,   990,    PMU_C,  2,		ZQM_PM_MAP(ah), },
	{   "F6",   -100,   -990,   990,    PMU_C,  2,		ZQM_PM_MAP(al), },
	{   "F7",   20,     0,      24,     PMU_H,  1,		ZQM_PM_MAP(f7), },
	{   "F8",   8,     	0,      24,     PMU_H,  1,		ZQM_PM_MAP(f8), },
	{   "F9",   0,     	0,      1,     	PMU_N,  1,		ZQM_PM_MAP(f9), },
	{   "Tr",   80,     60,     200,    PMU_C,  1,		ZQM_PM_MAP(tr), },
	{   "PS",   22,   	0,   	99,    	PMU_PS, 1,		ZQM_PM_MAP(ps), },
};
const uint8_t PM_NUM = (sizeof(pms_spec) / sizeof(ZqmPmSpec));
// uint32_t PM_NUM = sizeof(pms_spec) / sizeof(ZqmPmSpec);
ZqmPmIdx pm_idx = {ZQM_PM_IDX_NA};
ZqmPmCtrl pm_ctrl = {
		// 温度控制参数
		.ts = 50,	//设定温度; r1~r2; ℃
		.r1 = 20,	//设定温度最小值, n~TS ℃
		.r2 = 80,	//设定温度最大值, TS~n ℃
		.ad = 0,	//报警延时, min
		.ah = 150,	//高温报警值, ℃
		.al = 0,	//低温报警值, ℃
		.as = 0,	//上电报警延时, H
		// 压缩机/制热器控制参数
		.c1 = 20,	//温度上回差, .1℃
		.c2 = 20,	//温度下回差, .1℃
		.c3 = 0,	//传感器故障开机时间, M
		.c4 = 0,	//传感器故障停机时间, M
		.c5 = 0,	//显示温度补偿值, .1℃
		.c6 = 0,	//化霜显示解锁时间, H
		.c7 = 5,	//最小停机时间, M
		.c8 = 0,	//开停机补偿, ℃
		.cd = 0,	//上电延时启动 M
		.cf = 0,	//压机强制运行 H
		.mtc = 0,	//测量温度补偿 .1℃ 
		// 除霜参数 自然/电热化霜机型(1004)
		.d1 = 0,	//自然化霜/压机保护周期, H
		.d2 = 0,	//自然化霜/压机保护时间, M
		.d3 = 0,	//加热化霜-化霜周期, H
		.d4 = 0,	//自然/加热化霜-排水时间, M
		.d5 = 0,	//加热化霜-预冷时间, M
		.d6 = 0,	//加热化霜-退出温度, ℃
		.d7 = 0,	//加热化霜-上限时间, M
		.d8 = 0,	//强制手动化霜-时间, M
		// 功能参数
		.f1 = 0,	//故障代码查看
		.f2 = 0,	//4G信号强度查看, 0-31 
		.f3 = 0,	//电池电量查看, 0-100, % 
		.f4 = 0,	//软件版本, M0和-Cat1切换 
		.ps = 0,	// 密码配置
		.h4 = 0,	// 蜂鸣器开关
		.dm = 0,	// 显式模式参数
		.lc = 0,	// 拉温模式, 缺省值为关闭=0, 
		.f7 = 0,	//非营业模块开始时间, H
		.f8 = 0,	//非营业模块结束时间, H
		.f9 = 0,	//非营业模块智能模式, N
		.tr = 80,	//非营业模式设定温度, ℃
};

// define device IO
ZyIOsDefTable zy_ios = {
    // UART0_TX PB7; UART0_RX PB4; 
    .uart0_tx = ZyGPIO_B(7),    .uart0_rx = ZyGPIO_B(4),
    // UART1_TX PA9; UART1_RX PA8; 
    .uart1_tx = ZyGPIO_A(9),    .uart1_rx = ZyGPIO_A(8),
    // UART2_TX PB23; UART2_RX PA6; 
    .uart2_tx = ZyGPIO_B(23),   .uart2_rx = ZyGPIO_A(6),
    // UART3_TX PB21; UART3_RX PB20; 
    .uart3_tx = ZyGPIO_B(21),   .uart3_rx = ZyGPIO_B(20),
    // 外设电源控制 PB13; 
    .periph_pow = ZyGPIO_B(13),  
    // CAT1(通讯模块)控制脚 PB16; 
    .cat1 = ZyGPIO_B(16),        
    // 压机控制脚 PB18; 
    .motor = ZyGPIO_B(18),       
    // 蒸发风机控制脚 PB17; 
    .fan = ZyGPIO_B(17),
    // 冷凝风机控制脚 NA/PB17; 
    .condenser_fan = ZyGPIO_NA,
    // 制热控制脚 NA/PA12; 
    .heater = ZyGPIO_NA,
    // 照明控制脚 PA12; 
    .light = ZyGPIO_A(12),
    // 化霜加热/电磁阀控制脚 NA; 
    .melt = ZyGPIO_NA,
    // 蜂鸣器 NA; 
    .buzzor = ZyGPIO_NA,
    // 电源侦测脚 PA3; 
    .det_12v = ZyGPIO_A(3),
    // 系统开关侦测脚 NA/PA13; 
    .det_sys_switch = ZyGPIO_NA,
    // 门1侦测脚 PA2; 
    .det_door1 = ZyGPIO_A(2),
    // 门2侦测脚 NA; 
    .det_door2 = ZyGPIO_NA,
    // 红外人流侦测脚 NA/PA14; 
    .det_human = ZyGPIO_NA,
    // 传感器1 PA0; 
    .ntc1_adc = ZyGPIO_A(0),    
    // 传感器2 PA1; 
    .ntc2_adc = ZyGPIO_A(1),    
    // 电池电压侦测脚 PA15; 
    .batt_adc = ZyGPIO_A(15),   
    // 电池侦测/充电脚 PA10; 
    .batt_chrg = ZyGPIO_A(10),  
    // 电池待机脚 PA11; 
    .batt_stdby = ZyGPIO_A(11),
    // 显示驱动SPI数据线 PB0; 
    .dis_dio = ZyGPIO_B(0),
    // 显示驱动模拟SPI时钟线 PB1; 
    .dis_clk = ZyGPIO_B(1),
    // 显示驱动使能脚 PB2
    .dis_stb = ZyGPIO_B(2),
};

// 建立spec参数中[start, end]的映射表
static void PmInitMap(int start, int end)
{
	uint8_t* pidx = (uint8_t*)&pm_idx;
	memset(pidx, 0xff, sizeof(pm_idx));
	short* pval = (short*)&pm_ctrl;
	for (int i = start; i < end; i++) {
		pidx[pms_spec[i].map] = i;
		pval[pms_spec[i].map] = pms_spec[i].val;
	}
}
// 参数设置修正规则1: ts/r1/r2互相影响
static void PmSetValRule1(uint8_t idx, short value)
{	
	if (idx == pm_idx.ts) // 如果修改ST，要修正r1, r2的上下限		
		pms_spec[pm_idx.r2].min = pms_spec[pm_idx.r1].max = value;
	else if (idx == pm_idx.r1)  // 如果是R1, 修改相应的ST下限
		pms_spec[pm_idx.ts].min = value;
	else if (idx == pm_idx.r2) // 如果是R2, 修改相应的TS上限
		pms_spec[pm_idx.ts].max = value;
}
static void PmSetValRule2(void) 
{
	short TS = pm_ctrl.ts; // TODO - 不考虑ECO模式影响
#if (CFG_DEVICE_TYPE == 915)  // 特别冷藏/冷冻型, 根据温度设置配置修改参数指向位置
	if (TS >= 0 && pm_idx.c1 != 8) { // 冷藏模式，但是参数不是，修正
		zqm_cfg.ctrl_mode = zy_st.ctrl_mode = CTRL_MODE_COOL;			
		PmInitMap(8, 17); // 重建冷藏部分参数映射表
	}
	else if (TS < 0 && pm_idx.c1 != (8 + 10)) { // 冷冻模式，但是参数不是，修正
		zqm_cfg.ctrl_mode = zy_st.ctrl_mode = CTRL_MODE_FREEZE;
		PmInitMap(8+10, 17+10); // 重建冷藏部分参数映射表
	}
#endif
}
// set parameter value, return true if modified value in fact(used to just is need to save), or false
// idx: parameter index (pm_idx), value: new value
bool PmSetValue(uint8_t idx, short value)
{
	// 检查
	if (idx >= PM_NUM) return false;
	if(pms_spec[idx].unit >= PMU_R) return false;
	if (value == ZQ_INVALID_PARAM) return false;
	if (value == pms_spec[idx].val) return false;
	if (pms_spec[idx].unit == PMU_C) value = (value / 10) * 10; // 精度是1℃的参数, 舍掉个位
	if(value < pms_spec[idx].min || value > pms_spec[idx].max) return false;
	// set spec parameter value
	pms_spec[idx].val = value;
	// read only parameter, modified min/max same as value
	if(pms_spec[idx].unit == PMU_R) pms_spec[idx].min = pms_spec[idx].max = value;
	// 修正规则1: ts/r1/r2互相影响
	PmSetValRule1(idx, value);
	// 修正规则2: 冷藏/冷冻型(如915), ts变化会导致工作模式切换 
	PmSetValRule2();
	// set ctrl parameter value
	((short*)&pm_ctrl)[pms_spec[idx].map] = value;
	return true;
}
// force to set parameter value, without checking input value
// idx: parameter index (pm_idx), value: new value, min and max value
void PmForceSetValue(uint8_t idx, short value, short min, short max)
{
	if (idx >= PM_NUM) return;
	// set spec and ctrl parameter value
	((short*)&pm_ctrl)[pms_spec[idx].map] = pms_spec[idx].val = value;
	pms_spec[idx].min = min;
	pms_spec[idx].max = max;
	return;
}
// save all parameters to flash
#define MAX_PARAM_NUM 48
void PmSave(void)
{
	// vs studio c 编译器不支持这种写法，临时加入MAX_PARAM_NUM来规避
	//ZQ_ALIGNED(4) uint8_t buf[sizeof(zqm_cfg) + PM_NUM * sizeof(short)];
	ZQ_ALIGNED(4) uint8_t buf[sizeof(zqm_cfg) + MAX_PARAM_NUM * sizeof(short)];
	zqm_cfg.ctrl_switch = zy_st.ctrl_switch; // sync switch state
	zqm_cfg.ctrl_mode &= ~CTRL_MODE_ECO;	// eco mode is not saved
	memcpy(buf, &zqm_cfg, sizeof(zqm_cfg));
	uint8_t *buf2 = buf + sizeof(zqm_cfg);
	for (int i = 0; i < PM_NUM; i++)
		((short*)buf2)[i] = pms_spec[i].val;
	EEPROM_ERASE(ZQM_CFG_FILE_ADDR, ZQM_CFG_FILE_SIZE);
	EEPROM_WRITE(ZQM_CFG_FILE_ADDR, buf, sizeof(zqm_cfg) + PM_NUM * sizeof(short));
	return;
}
// load all parameters from flash
void PmLoad(void)
{
	// 加载参数文件
	// vs studio c 编译器不支持这种写法，临时加入MAX_PARAM_NUM来规避
	//ZQ_ALIGNED(4) uint8_t buf[sizeof(zqm_cfg) + PM_NUM * sizeof(short)];
	ZQ_ALIGNED(4) uint8_t buf[sizeof(zqm_cfg) + MAX_PARAM_NUM * sizeof(short)];
	EEPROM_READ(ZQM_CFG_FILE_ADDR, buf, sizeof(buf));
	ZqHexDump("Load Parameters:", buf, sizeof(buf));
	ZqmCfgFile *cfg = (ZqmCfgFile*)buf;
	if (cfg->f1 != 'Z' || cfg->f2 != 'Q' || cfg->f3 != 'M' || cfg->ver != ZQM_CFG_FILE_VER) { // flag is invalid, or version error(ex. ota update), save default value
		PmSave();
	}
	else if(strcmp(cfg->model, AKM_BLE_NAME) != 0) { // model is not same, save default value
		PmSave();
	}
	else { // 加载存储的 local config 和 参数		
		memcpy(&zqm_cfg, cfg, sizeof(zqm_cfg));
		uint8_t *buf2 = buf +sizeof(zqm_cfg);
		for(int i = 0; i < PM_NUM; i++)
			pms_spec[i].val = ((short*)buf2)[i];
	}
	// 初始化映射表
	PmInitMap(0, PM_NUM);
	// 修正规则1: ts/r1/r2互相影响
	PmSetValRule1(pm_idx.r1, pm_ctrl.r1);
	PmSetValRule1(pm_idx.r2, pm_ctrl.r2);
	// 修正规则2: 冷藏/冷冻型(如915), ts变化会导致工作模式切换 
	PmSetValRule2();
	return;
}

short pm_ur_num[2];		// 数字形式的联合版本号，m0-cat1 类似10200-10500
// load the version code value
void PmLoadUrCode(void)
{
	// 建立PM_Ur版本号参数
	if (pm_ur_num[0] == 0) 
		pm_ur_num[0] = (ZQ_SW_VER[0] - 0x30) * 100 + (ZQ_SW_VER[2] - 0x30) * 10 + (ZQ_SW_VER[4] - 0x30);
	if(pm_ur_num[1] == 0 && cat1_sw_ver[0] != 0)
		pm_ur_num[1] = -((cat1_sw_ver[0] - 0x30) * 100 + (cat1_sw_ver[2] - 0x30) * 10 + (cat1_sw_ver[4] - 0x30));
}
// - 0	无故障	E0
// - 1	冷藏传感器开路或短路故障	E2
// - 2	冷冻传感器开路或短路故障	E3
// - 3	无效或非法的 SIM 卡		E5
// - 4	无法连接云平台服务器		E7
// - 5	MCU 通信故障	EA
// - 6	WIFI 模块通信故障	Eb
// - 7	蓝牙模块通信故障	Ec
// - 8	485摄像头故障	Ed
// - 9	IP摄像头故障	EE
char* pm_f1_val[10] = { "E0", };	// 参数F1(err code)的显示内容集合
// Dynamically load the error code value
void PmLoadErrCode(void)
{
	short idx = 0;
	if ((zy_st.ctrl_mode & CTRL_MODE_COOL) && (zy_st.alarm & ZQ_ALARM_SENSOR))			// 冷藏传感器开路或短路故障	E2
		pm_f1_val[idx++] = "E2";
	else if ((zy_st.ctrl_mode & CTRL_MODE_FREEZE) && (zy_st.alarm & ZQ_ALARM_SENSOR))	// 冷冻传感器开路或短路故障	E3
		pm_f1_val[idx++] = "E3";
	if (zy_st.alarm & ZQ_ALARM_DEFROST_SENSOR)											// 化霜传感器开路或短路故障	E6
		pm_f1_val[idx++] = "E6";
	//if ((zy_st.sim == 0))			// 无效或非法的 SIM 卡	E5
	//	pm_f1_val[idx++] = " E5";
	if (ZQ_COMM_PLATFORM != 582 && (zy_st._4g != MODE_4G_CONNECT)) 						// 无法连接云平台服务器	E7
		pm_f1_val[idx++] = "E7";
#if ((CFG_DEVICE_TYPE/100) == 24326)
	if (zy_st.cat1_heart < 50 * 1000) 	//5	MCU 通信故障	EA - 心跳是1 minute, 放宽10s，小于50s表示通信中断
		pm_f1_val[idx++] = "EA";
	if ((zy_st.wifi == 0))				//6	WIFI 模块通信故障	Eb
		pm_f1_val[idx++] = "Eb";
#endif
	//if ((zy_st.blue == 0))				//7	蓝牙模块通信故障	Ec
	//	pm_f1_val[idx++] = " Ec";
	//if ((zy_st.camera == 0))				//8	485摄像头故障	Ed
	//	pm_f1_val[idx++] = " Ed";
	//if ((zy_st.ip_camera == 0))			//9	IP摄像头故障	EE
	//	pm_f1_val[idx++] = " EE";
	if (idx == 0)
		pm_f1_val[idx++] = "E0";			// no err
#if (CFG_DEVICE_TYPE == 1625 || CFG_DEVICE_TYPE == 760) 			// 1625/760 附加信号强度
	static char pm_ec_signal[8];
	sprintf(pm_ec_signal, "C%02d", zy_st._4g_strength);
	pm_f1_val[idx++] = pm_ec_signal;
#endif
	PmForceSetValue(pm_idx.f1, 0, 0, idx);
	return;
}

// 本地存储数据 ===================================================================================================
// M0 flash local config file default value
ZqmCfgFile zqm_cfg = {
	'Z', 'Q', 'M', ZQM_CFG_FILE_VER, 
	{AKM_BLE_NAME}, 
	CTRL_ALL_SWITCH, 
	ZQ_BOX_MODE, 
	{0},
};

// 软件版本定义以及改动点记录 ===================================================================================================
// 软件版本定义 ---------------------------------------
// set a fixed string in image, like "***AUTO_VER_M0:3001001036++AAKM_V1.0.2.00", 供自动化版本工具使用, 要求:
// 1. "***AUTO_VER:" - 开头固定特征串, 长度12
// 2. "AKM_BLE_NAME+" - 蓝牙名称, 长度12, 不足的用"+"填充, 至少1个"+"
// 3. "ZQ_VER_PREFIX"M_V"ZQ_M0_HW_VER" - 版本前缀加"M_V"加版本号, 版本号从1.0.0.00开始, 固定格式a.b.c.de
const char * ZQ_AUTO_SW_VER = "***AUTO_VER:"AKM_BLE_NAME""AKM_BLE_NAME_SUFFIX"+"ZQ_VER_PREFIX"M_V""1.0.0.00";
const char * ZQ_AUTO_HW_VER = ZQ_VER_PREFIX"H_V""1.0.0.00";

// 版本修改记录 ---------------------------------------
/*
## M0版本修改记录:
V1.0.0: -- 开始HW测试版本

*/

// 版本演进计划 ---------------------------------------
/*
* task 启动发送init消息，后续才是定时启动
* 
*/
